﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using Microsoft.Win32;

namespace System.Peppers.Win32
{
    [SuppressUnmanagedCodeSecurity]
    internal class GdiPlus
    {
        protected const string DLL = "gdiplus.dll";

        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        internal static extern int GdipCreateBitmapFromStream(IStream stream, out IntPtr bitmap);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        internal static extern int GdipImageForceValidation(HandleRef image);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern void GdiplusNotificationUnhook(HandleRef token);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern void GdiplusShutdown(HandleRef token);
        [DllImport(DLL, CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int GdiplusNotificationHook(out IntPtr token);
        [DllImport(DLL, EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int IntGdipDisposeImage(HandleRef image);

        internal static int GdipDisposeImage(HandleRef image)
        {
            if (_isShutdown)
            {
                return 0;
            }
            return IntGdipDisposeImage(image);
        }

        private static IntPtr hookToken = IntPtr.Zero,
            initToken = IntPtr.Zero;
        private static bool _isShutdown = true;

        static GdiPlus()
        {
            Initialize();
        }

        private static void Initialize()
        {
            bool flag = Environment.UserInteractive;
            StartupOutput output;
            StartupInput input = StartupInput.GetDefault();
            if (flag)
            {
                input.SuppressBackgroundThread = true;
            }
            int status = GdiplusStartup(out initToken, ref input, out output);
            if (status != 0)
            {
                throw ToGdiException(status);
            }
            if (flag && (Thread.GetDomain().GetData(".appDomain") == null))
            {
                SystemEvents.InvokeOnEventsThread(new EventHandler(SystemEventThreadCallback));
            }
            _isShutdown = false;
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
            if (flag && (Thread.GetDomain().GetData(".appDomain") == null))
            {
                SystemEvents.EventsThreadShutdown += new EventHandler(OnSystemEventThreadShutdown);
            }
        }

        private static void OnProcessExit(object sender, EventArgs e)
        {
            Shutdown();
        }

        private static void OnSystemEventThreadShutdown(object sender, EventArgs e)
        {
            if (hookToken != IntPtr.Zero)
            {
                GdiplusNotificationUnhook(new HandleRef(null, hookToken));
            }
        }

        private static void Shutdown()
        {
            if (!_isShutdown)
            {
                Thread.SetData(Thread.GetNamedDataSlot("system.drawing.threaddata"), null);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                if (initToken != IntPtr.Zero)
                {
                    GdiplusShutdown(new HandleRef(null, initToken));
                }
                _isShutdown = true;
            }
        }

        internal static Exception ToGdiException(int status)
        {
            return new Exception("Error in GDI+: " + status);
        }

        private static void SystemEventThreadCallback(object sender, EventArgs e)
        {
            GdiplusNotificationHook(out hookToken);
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct StartupInput
        {
            public int GdiplusVersion;
            public IntPtr DebugEventCallback;
            public bool SuppressBackgroundThread;
            public bool SuppressExternalCodecs;
            public static GdiPlus.StartupInput GetDefault()
            {
                GdiPlus.StartupInput input = new GdiPlus.StartupInput();
                input.GdiplusVersion = 1;
                input.SuppressBackgroundThread = false;
                input.SuppressExternalCodecs = false;
                return input;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct StartupOutput
        {
            public IntPtr hook;
            public IntPtr unhook;
        }

        protected GdiPlus() { }
    }
}
